iT邦幫忙

2025 iThome 鐵人賽

DAY 13
1
Modern Web

原生元件養成計畫:Web Component系列 第 13

Day 13: Web Component 的插槽樣式 ::slotted

  • 分享至 

  • xImage
  •  

在昨天的文章裡,我們介紹了三種從外部改變 Shadow DOM 樣式的方法。今天要繼續延伸這個主題,來看看另一種改變樣式的技巧。

還記得前兩天學過的 slot(插槽) 嗎?這次要介紹的,就是如何調整插入 slot 裡的子元素樣式。

::slotted

::slotted 是一種 CSS 的偽元素選擇器,可以在元件的 Shadow DOM 中,選擇外部插入 slot 的元素。
他可以幫助我們先替外部插入的內容設定一個預設樣式,確保使用者在元件初始使用時有一致的外觀。

使用場景

當你想要給使用者插入的內容一個初始統一的外觀,例如彈窗的標題、內容、按鈕區域。
適合用來提供基礎樣式(像是間距、字體大小、顏色),而不是一個強制的設計規範。
如果 UI 是要嚴格訂定的樣式的話(例如設計系統中的標準化按鈕),那就不適合只靠 ::slotted()

特性

  1. 提供預設樣式。
  2. 維持元件整體一致性。
  3. 保留使用者的彈性(使用者仍能透過 inline-style 或 class 覆蓋樣式)。

如何更改 slot 中的樣式

可以使用 ::slotted([slot="名稱"]) 來賦予指定 slot 的基礎樣式。
我們試著用前面建立的 custom-modal 來加入預設樣式看看!

  1. 首先利用 ::slotted 加入預設的樣式
    modal.js
class CustomModal extends HTMLElement {
  constructor() {
    super();

    const shadowRoot = this.attachShadow({ mode: 'open' });
    const cloneNode = this.render().cloneNode(true);
    shadowRoot.appendChild(cloneNode);
  }
  
  render() {
    const template = document.createElement('template');
    template.innerHTML = `
      <style>
        .modal-container {
          min-width: 150px;
          padding: 20px;
          background-color: #dddddd;
          border-radius: 6px;
        }
        
        .modal-header {
          font-size: 20px;
          font-weight: 500;
          padding-bottom: 4px;
        }

        .modal-content {
          padding: 4px;
          border-top: 1px solid #999999;
          border-bottom: 1px solid #999999;
        }

        ::slotted([slot="header"]) {
          padding: 8px;
          font-size: 16px;
          color: #7361de;
        }

        ::slotted([slot="content"]) {
          padding: 8px;
          font-size: 12px;
          color: #333173;
        }

        ::slotted([slot="footer"]) {
          display: flex;
          padding: 4px;
          background-color: #ffffff;
        }
      </style>

      <div class="modal-container">
        <div class="modal-header">
          <slot name="header"></slot>
        </div>

        <div class="modal-content">
          <slot name="content">
            <p class="default-content">預設文字</p>
          </slot>
        </div>

        <div class="modal-footer">
          <slot name="footer"></slot>
        </div>
      </div>
    `

    return template.content;
  }
}

customElements.define('custom-modal', CustomModal);
  1. 在外部插入自訂內容
    index.html
<custom-modal>
  <div slot="header">
    <p>插入的 header</p>
  </div>

  <div slot="content">彈出視窗的內容</div>

  <div slot="footer">
    <button>只能按確定!</button>
  </div>
</custom-modal>

得到的結果:
slot

這時你可能會想:如果我不喜歡元件的預設樣式,能不能改掉?
答案是 可以的! 只要在外部加上 class 或 inline-style,就可以輕鬆覆蓋掉原本的樣式。

index.html

<custom-modal>
  <div slot="header" style="background-color: #eeeeee;">
    <p>插入的 header</p>
  </div>

  <div slot="content" style="padding: 16px 8px">彈出視窗的內容</div>

  <div slot="footer" style="padding: 12px 8px; border-radius: 6px">
    <button>只能按確定!</button>
  </div>
</custom-modal>

得到的結果:
slot

以上,就是 ::slotted 的基本使用方法!

說說 ::slotted 不實用的地方

  1. 只能選中直接子元素本身。
    為了避免 Shadow DOM 的封裝被破壞,::slotted 主要是讓元件能控制插槽的直接內容,而不是深入的去改外部結構。
    所以 ::slotted() 只能選中被插入 slot 的那個直接子元素本身,沒辦法指定哪個 slot,只能靠插進來的元素屬性(例如 [slot="title"])去區分。(你不能用 ::slotted( a > b) 直接選擇更內部的元素)。
::slotted(div p){}  /* 錯誤的用法 */
  1. 外部覆蓋樣式容易。
    如果你希望外部插入的東西完全長的一樣,像是設計師設定的 padding 或是 font-size 等等需要一致化的 UI 介面。那麼 ::slotted() 可能幫不上忙,因為外部可以簡單的就覆蓋掉。
  2. 難以預測外部結構。
    不要依賴它做強制一致性的排版,你不知道使用者會插入什麼元素,有可能是 <p><div>、又或是你根本沒想過的 <table>
    如果外部結構太自由,::slotted() 提供的預設樣式就變得沒什麼幫助。

樣式設定方式的統一小結

  • ::slotted:適合並不是嚴謹樣式的元件使用,可以替插入 slot 的元素設定基礎樣式,且可以被外部覆蓋。
  • ::host():適合不希望被外部輕易修改樣式的元件使用,可以針對元件定義預設樣式(例如:primary、secondary、small...),外部透過在 host 上加 class 或屬性來改變樣式。
  • CSS Variable:適合讓外部定義統一規範的元件,透過可繼承的變數統一管理顏色、間距等設計樣式。
  • ::part():適合想開放部分樣式給外部的元件,如果希望外部可以更彈性的控制元件內部樣式,可以在元件內標記 part,外部使用者使用 ::part() 選擇對應的元素來改變樣式。

上一篇
Day 12: Web Component 的樣式 style
系列文
原生元件養成計畫:Web Component13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言